﻿using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Data.SqlServerCe;

namespace CalcSharp.Core.NumberCache
{
    public class CacheData
    {
        private Cache database;
        private bool useable;

        public CacheData()
        {
            this.database = new Cache(Path.GetDirectoryName(Application.ExecutablePath) + @"\cache.sdf");
            if (database.DatabaseExists())
            {
                try
                {
                    if (database.Factorialtable.Count() > 0 &&
                        database.FibonacciCache.Count() > 0 &&
                        database.Primetable.Count() > 0
                        ) useable = true;
                    else useable = false;
                }
                catch (SqlCeException) { useable = false; }
            }
            else useable = false;
        }

        public void Close()
        {
            if (database != null)
            {
                this.database.Dispose();
                this.database = null;
                GC.Collect();
            }
        }

        public bool Usable
        {
            get { return useable; }
        }

        public bool Exists
        {
            get { return this.database.DatabaseExists(); }
        }

        public void Initialize()
        {
            if (!database.DatabaseExists())
            {
                database.CreateDatabase();
                this.filldatabase();
            }
            else
            {
                database.DeleteDatabase();
                database.CreateDatabase();
                this.filldatabase();
            }
        }

        private void filldatabase()
        {
            if (!this.Exists) throw new CSException("Cache database must be Initilaized first");
            int i, c;
            double j;
            double last = 1;
            bool prime;
            double n0, n1, val;
            Factorialtable f;
            Primetable p;
            FibonacciCache fi;

            #region Factorial fill
            for (i = 1; i <= 170; i++)
            {
                last = last * i;
                f = new Factorialtable();
                f.Number = i;
                f.Factorial = last;
                database.Factorialtable.InsertOnSubmit(f);
            }
            database.SubmitChanges();
            #endregion
            i = 0;

            #region Prime fill
            p = new Primetable();
            p.Number = 2;
            p.Prime = true;
            database.Primetable.InsertOnSubmit(p);

            c = 1;
            for (i = 3; i < int.MaxValue; i += 2)
            {
                prime = true;
                for (j = 2; j <= Math.Sqrt(i); j++)
                {
                    if (i % j == 0)
                    {
                        prime = false;
                        break;
                    }
                }
                if (prime)
                {
                    p = new Primetable();
                    p.Number = i;
                    p.Prime = true;
                    database.Primetable.InsertOnSubmit(p);
                    c++;
                }
                if (c == 10000) break;
            }
            database.SubmitChanges();
            #endregion

            fi = new FibonacciCache();
            fi.Number = 0;
            fi.Fibonacci = 0;
            database.FibonacciCache.InsertOnSubmit(fi);
            fi = new FibonacciCache();
            fi.Number = 1;
            fi.Fibonacci = 1;
            database.FibonacciCache.InsertOnSubmit(fi);
            fi = new FibonacciCache();
            fi.Number = 2;
            fi.Fibonacci = 1;
            database.FibonacciCache.InsertOnSubmit(fi);

            n0 = n1 = val = 1;

            for (i = 3; i <= 1000; i++)
            {
                val = n0 + n1;
                n0 = n1;
                n1 = val;
                fi = new FibonacciCache();
                fi.Number = i;
                fi.Fibonacci = val;
                database.FibonacciCache.InsertOnSubmit(fi);
            }
            database.SubmitChanges();
        }

        public bool GetPrime(double number)
        {
            if (!this.Usable) throw new CSException("Cache isn't initialized");
            var query = from data in database.Primetable where data.Number == number select data.Prime;
            if (query.Count() < 1) return false;
            else return query.First();
        }

        public double GetLastPrime()
        {
            if (!this.Usable) throw new CSException("Cache isn't initialized");
            return database.Primetable.Max(num => num.Number);
        }

        public double GetFactorial(double number)
        {
            if (!this.Usable) throw new CSException("Cache isn't initialized");
            if (number > 170) throw new CSException("Max factorial value is 170");
            var query = from data in database.Factorialtable where data.Number == number select data.Factorial;
            return query.First();
        }

        public double GetFibonacci(double number)
        {
            if (!this.Usable) throw new CSException("Cache isn't initialized");
            var query = from data in database.FibonacciCache where data.Number == number select data.Fibonacci;
            return query.First();
        }
    }
}
